// DefaultServlet.java - A file GETting servlet.
//
// Copyright (C) 2001-2002  Smart Software Consulting
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Smart Software Consulting
// 1688 Silverwood Court
// Danville, CA  94526-3079
// USA
//
// http://www.smartsc.com
//

package com.smartsc.http;

import java.io.*;
import java.util.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class DefaultServlet
extends HttpServlet
implements HttpReasonPhrases, HttpStatusCodes
{
	public void init()
	throws ServletException
	{
		ServletContext server = getServletContext();
		if( !(server instanceof HttpServer) )
		{
			throw new ServletException(
				"servletContext is not com.smartsc.HttpServer" );
		}
		this.server = (HttpServer)server;
		indexFilename = getInitParameter( PROP_INDEX_FILE );
		if( indexFilename == null )
		{
			indexFilename = DEFAULT_INDEX_FILE;
		}
	}

	public void doGet( HttpServletRequest req, HttpServletResponse res )
	throws ServletException, IOException
	{
		if( !(req instanceof HttpRequest)
		||  !(res instanceof HttpResponse) )
		{
			throw new ServletException(
				new HttpException(
					SC_INTERNAL_SERVER_ERROR, RP_INTERNAL_SERVER_ERROR ));
		}

		try { doGet( (HttpRequest)req, (HttpResponse)res ); }
		catch( HttpException he )
		{
			throw new ServletException( he );
		}
	}

	protected
	void
	doGet( HttpRequest req, HttpResponse res)
	throws HttpException
	{
		// Get request URI (ignore any query string)
		String path = req.getRequestURI();

		// Build filename to determine MIME type
		String filename = req.getRealPath( path);
		File file = new File( filename);

		// If directory
		if( file.isDirectory())
		{
			// Use index filename
			filename = indexFilename;
			file = new File( file, filename);
		}

		// If file is not under doc root
		if( !file.getAbsolutePath().startsWith( server.getDocRoot()) )
		{
			throw new HttpException( SC_FORBIDDEN, RP_FORBIDDEN);
		}

		// If file does not exist
		if( !file.exists())
		{
			if( notFoundHttpException == null )
			{
				notFoundHttpException =
					new HttpException( SC_NOT_FOUND, RP_NOT_FOUND );
			}
			throw notFoundHttpException;
		}

		// Set Last Modified header, if not already set
		long lastModified = file.lastModified();
		if( !res.containsHeader( HttpHeaders.LAST_MODIFIED))
		{
			res.setDateHeader( HttpHeaders.LAST_MODIFIED, lastModified);
		}

		long ifModifiedSince =
			req.getDateHeader( HttpHeaders.IF_MODIFIED_SINCE);

		if( ifModifiedSince >= 0)
		{
			long now = System.currentTimeMillis();

			// The ifModifiedSince header only has 1 second
			// precision.  We must add 999 ms to it for comparison
			// with lastModified, otherwise only 1 out of every
			// thousand unchanged files would have equal times.
			if( lastModified <= ifModifiedSince + 999
			&&  ifModifiedSince <= now )
			{
				// Send "Not Modified" response
				res.sendError( HttpStatusCodes.SC_NOT_MODIFIED);
				// Done!
				return;
			}
		}

		// Get input stream for request path
		FileInputStream is = null;
		try { is = new FileInputStream(file); }
		catch( FileNotFoundException fnfe)
		{
			if( notFoundHttpException == null )
			{
				notFoundHttpException =
					new HttpException( SC_NOT_FOUND, RP_NOT_FOUND );
			}
			throw notFoundHttpException;
		}

		// Set content type
		try { res.setContentType( server.getContentTypeFor( filename)); }
		catch( IllegalStateException ise) {}

		try { res.setContentLength( is.available()); }
		catch( IOException ioe)
		{
			throw new HttpException(
				SC_INTERNAL_SERVER_ERROR, RP_INTERNAL_SERVER_ERROR);
		}

		OutputStream os = null;
		try
		{
			os = res.getOutputStream();
			byte[] buf = new byte[server.getBufferSize()];

			int bytesRead = is.read( buf);
			while( bytesRead != -1 )
			{
				os.write( buf, 0, bytesRead);
				bytesRead = is.read( buf);
			}
		}
		catch( IOException ioe)
		{
			log( "IOException sending static content", ioe);
		}
		finally
		{
			try
			{
				is.close();
				os.close();
			}
			catch( IOException e) {}
		}
	}

	protected HttpServer server;
	protected String indexFilename;
	protected static HttpException notFoundHttpException;

	public static final String DEFAULT_INDEX_FILE = "index.html";
	public static final String PROP_INDEX_FILE    = "indexFile";
}